package com.sca4cloud.sca.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sca4cloud.sca.common.core.constant.CacheConstants;
import com.sca4cloud.sca.common.core.constant.CommonConstants;
import com.sca4cloud.sca.common.core.constant.SecurityConstants;
import com.sca4cloud.sca.common.core.constant.enums.CaptchaFlagTypeEnum;
import com.sca4cloud.sca.common.core.exception.ValidateCodeException;
import com.sca4cloud.sca.common.core.util.SpringContextHolder;
import com.sca4cloud.sca.common.core.util.WebUtils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

/**
 * 验证码处理
 */
@Slf4j
@Component
@AllArgsConstructor
@WebFilter(urlPatterns = "/*")
public class ValidateCodeGatewayFilter implements Filter {
    private final ObjectMapper objectMapper;
    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        String url = request.getRequestURI();
        // 不是登录请求，直接向下执行
        if (!StrUtil.containsAnyIgnoreCase(url, SecurityConstants.OAUTH_TOKEN_URL, SecurityConstants.SMS_TOKEN_URL)) {
            chain.doFilter(servletRequest, servletResponse);
            return;
        }

        // 刷新token，直接向下执行
        String grantType = request.getParameter("grant_type");
        if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, grantType)) {
            chain.doFilter(servletRequest, servletResponse);
            return;
        }

        // 终端设置不校验， 直接向下执行
        try {
            // 判断客户端是否跳过检验
            if (!isCheckCaptchaClient(request)) {
                chain.doFilter(servletRequest, servletResponse);
                return;
            }
            // 校验验证码
            checkCode(request);
            chain.doFilter(servletRequest, servletResponse);
        } catch (Exception e) {
            log.error("{}", e);
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.setStatus(HttpStatus.PRECONDITION_REQUIRED.value());
        }

    }

    /**
     * 是否需要校验客户端，根据client 查询客户端配置
     * 
     * @param request
     *            请求
     * @return true 需要校验， false 不需要校验
     */
    private boolean isCheckCaptchaClient(HttpServletRequest request) {
        String header = request.getHeader(HttpHeaders.AUTHORIZATION);
        String clientId = WebUtils.extractClientId(header).orElse(null);
        // 获取租户拼接区分租户的key
        String tenantId = request.getHeader(CommonConstants.TENANT_ID);
        String key = String.format("%s:%s:%s", StrUtil.isBlank(tenantId) ? CommonConstants.TENANT_ID_1 : tenantId,
            CacheConstants.CLIENT_FLAG, clientId);

        Object val = redisTemplate.opsForValue().get(key);

        // 当配置不存在时，默认需要校验
        if (val == null) {
            return true;
        }

        JSONObject information = JSONUtil.parseObj(val.toString());
        if (StrUtil.equals(CaptchaFlagTypeEnum.OFF.getType(), information.getStr(CommonConstants.CAPTCHA_FLAG))) {
            return false;
        }
        return true;
    }

    /**
     * 检查code
     *
     * @param request
     */
    @SneakyThrows
    private void checkCode(HttpServletRequest request) {
        String code = request.getParameter("code");

        if (StrUtil.isBlank(code)) {
            throw new ValidateCodeException("验证码不能为空");
        }

        String randomStr = request.getParameter("randomStr");
        // 若是滑块登录
        if (CommonConstants.IMAGE_CODE_TYPE.equalsIgnoreCase(randomStr)) {
            CaptchaService captchaService = SpringContextHolder.getBean(CaptchaService.class);
            CaptchaVO vo = new CaptchaVO();
            vo.setCaptchaVerification(code);
            if (!captchaService.verification(vo).isSuccess()) {
                throw new ValidateCodeException("验证码不能为空");
            }
            return;
        }

        String mobile = request.getParameter("mobile");
        if (StrUtil.isNotBlank(mobile)) {
            randomStr = mobile;
        }

        String key = CacheConstants.DEFAULT_CODE_KEY + randomStr;

        if (!redisTemplate.hasKey(key)) {
            throw new ValidateCodeException("验证码不合法");
        }

        Object codeObj = redisTemplate.opsForValue().get(key);

        if (codeObj == null) {
            throw new ValidateCodeException("验证码不合法");
        }

        String saveCode = codeObj.toString();
        if (StrUtil.isBlank(saveCode)) {
            redisTemplate.delete(key);
            throw new ValidateCodeException("验证码不合法");
        }

        if (!StrUtil.equalsIgnoreCase(saveCode, code)) {
            redisTemplate.delete(key);
            throw new ValidateCodeException("验证码不合法");
        }

        redisTemplate.delete(key);
    }
}
